export class GameController {
  cellArr: number[][] = []
  isGameOver: boolean = false

  startGame() {
    this.cellArr = [
      [0, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 0],
      [0, 0, 0, 0]
    ]
    this.isGameOver = false
    this.fillOneEmptyCell()
    this.fillOneEmptyCell()
  }

  fillOneEmptyCell() {
    const emptyCells = this.findEmptyCells()
    const index = this.random(emptyCells.length)
    const [row, col] = emptyCells[index]
    this.cellArr[row][col] = this.getRandomValue()
  }

  findEmptyCells(): [number, number][] {
    const result: [number, number][] = []
    for (let i = 0; i < 4; i++) {
      for (let j = 0; j < 4; j++) {
        if (this.cellArr[i][j] === 0) {
          result.push([i, j])
        }
      }
    }
    return result
  }

  getRandomValue() {
    if (Math.random() < 0.9) {
      return 2
    } else {
      return 4
    }
  }

  random(max: number) {
    return Math.floor(Math.random() * max)
  }

  canMove() {
    for (let i = 0; i < 4; i++) {
      for (let j = 0; j < 4; j++) {
        if (this.cellArr[i][j] == 0) {
          return true
        }
        if (j < 3 && this.cellArr[i][j] == this.cellArr[i][j + 1]) {
          return true
        }
        if (i < 3 && this.cellArr[i][j] == this.cellArr[i + 1][j]) {
          return true
        }
      }
    }
    return false
  }

  removeZerosAndAdd(cells: number[], isForward: boolean) {
    const nonZeroCells = cells.filter(x => x !== 0)
    const mergedCells = this.mergeCells(nonZeroCells, isForward)
    const result = [0, 0, 0, 0]
    if (isForward) {
      result.splice(0, mergedCells.length, ...mergedCells)
    } else {
      result.splice(4 - mergedCells.length, mergedCells.length, ...mergedCells)
    }
    return result
  }

  mergeCells(cells: number[], isForward: boolean): number[] {
    if (!isForward) {
      cells = cells.reverse()
    }
    let result: number[] = []
    let i = 0
    while (i < cells.length) {
      if (i < cells.length - 1 && cells[i] === cells[i + 1]) {
        result.push(cells[i] * 2)
        i += 2
      } else {
        result.push(cells[i])
        i += 1
      }
    }
    if (!isForward) {
      result = result.reverse()
    }
    return result
  }

  move(direction: string) {
    this.moveCells(direction)
    this.checkGameOverOrContinue()
  }

  moveCells(direction: string) {
    const isHorizontal = direction === 'left' || direction === 'right'
    const isForward = direction === 'left' || direction === 'up'

    for (let i = 0; i < 4; i++) {
      let cells: number[] = []
      if (isHorizontal) {
        cells = this.cellArr[i]
      } else {
        for (let j = 0; j < 4; j++) {
          cells.push(this.cellArr[j][i])
        }
      }
      const result = this.removeZerosAndAdd(cells, isForward)
      if (isHorizontal) {
        this.cellArr[i] = result
      } else {
        for (let j = 0; j < result.length; j++) {
          this.cellArr[j][i] = result[j]
        }
      }
    }
  }

  checkGameOverOrContinue() {
    if (this.canMove()) {
      this.fillOneEmptyCell()
    } else {
      this.isGameOver = true
    }
  }

  getFlatCellArr(): number[] {
    return this.cellArr.flat()
  }
}